#include "StdAfx.h"
#include "WilliamsMachine.h"

//mame/video/williams.c
void CWilliamsMachine::blit_pixel(int offset, int srcdata, int data, int mask, int solid)
{
	/* always read from video RAM regardless of the bank setting */
	int pix = (offset < 0x9800) ? VRAM[offset] : MainCPU->Read(offset);

	/* handle transparency */
	if (data & 0x08)
	{
		if (!(srcdata & 0xf0)) mask |= 0xf0;
		if (!(srcdata & 0x0f)) mask |= 0x0f;
	}

	/* handle solid versus source data */
	pix &= mask;
	if (data & 0x10)
		pix |= solid & ~mask;
	else
		pix |= srcdata & ~mask;

	/* if the window is enabled, only blit to videoram below the clipping address */
	/* note that we have to allow blits to non-video RAM (e.g. tileram) because those */
	/* are not blocked by the window enable */
	//if (!williams_blitter_window_enable || offset < williams_blitter_clip_address || offset >= 0xc000)
	if(offset < 0x9800)
		MainCPU->Write(offset, pix);
}

static u8 blitter_remap[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};

int CWilliamsMachine::blitter_core(int sstart, int dstart, int w, int h, int data)
{
	int source, sxadv, syadv;
	int dest, dxadv, dyadv;
	int i, j, solid;
	int accesses = 0;
	int keepmask;

	/* compute how much to advance in the x and y loops */
	sxadv = (data & 0x01) ? 0x100 : 1;
	syadv = (data & 0x01) ? 1 : w;
	dxadv = (data & 0x02) ? 0x100 : 1;
	dyadv = (data & 0x02) ? 1 : w;

	/* determine the common mask */
	keepmask = 0x00;
	if (data & 0x80) keepmask |= 0xf0;
	if (data & 0x40) keepmask |= 0x0f;
	if (keepmask == 0xff)
		return accesses;

	/* set the solid pixel value to the mask value */
	solid = BlitterMask;

	/* first case: no shifting */
	if (!(data & 0x20))
	{
		/* loop over the height */
		for (i = 0; i < h; i++)
		{
			source = sstart & 0xffff;
			dest = dstart & 0xffff;

			/* loop over the width */
			for (j = w; j > 0; j--)
			{
//				blit_pixel(dest, blitter_remap[MainCPU->Read(source)], data, keepmask, solid);
				blit_pixel(dest, MainCPU->Read(source), data, keepmask, solid);
				accesses += 2;

				/* advance */
				source = (source + sxadv) & 0xffff;
				dest   = (dest + dxadv) & 0xffff;
			}

			sstart += syadv;

			/* note that PlayBall! indicates the X coordinate doesn't wrap */
			if (data & 0x02)
				dstart = (dstart & 0xff00) | ((dstart + dyadv) & 0xff);
			else
				dstart += dyadv;
		}
	}

	/* second case: shifted one pixel */
	else
	{
		/* swap halves of the keep mask and the solid color */
		keepmask = ((keepmask & 0xf0) >> 4) | ((keepmask & 0x0f) << 4);
		solid = ((solid & 0xf0) >> 4) | ((solid & 0x0f) << 4);

		/* loop over the height */
		for (i = 0; i < h; i++)
		{
			int pixdata;

			source = sstart & 0xffff;
			dest = dstart & 0xffff;

			/* left edge case */
//			pixdata = blitter_remap[MainCPU->Read(source)];
			pixdata = MainCPU->Read(source);
			blit_pixel(dest, (pixdata >> 4) & 0x0f, data, keepmask | 0xf0, solid);
			accesses += 2;

			source = (source + sxadv) & 0xffff;
			dest   = (dest + dxadv) & 0xffff;

			/* loop over the width */
			for (j = w - 1; j > 0; j--)
			{
//				pixdata = (pixdata << 8) | blitter_remap[MainCPU->Read(source)];
				pixdata = (pixdata << 8) | MainCPU->Read(source);
				blit_pixel(dest, (pixdata >> 4) & 0xff, data, keepmask, solid);
				accesses += 2;

				source = (source + sxadv) & 0xffff;
				dest   = (dest + dxadv) & 0xffff;
			}

			/* right edge case */
			blit_pixel(dest, (pixdata << 4) & 0xf0, data, keepmask | 0x0f, solid);
			accesses++;

			sstart += syadv;

			/* note that PlayBall! indicates the X coordinate doesn't wrap */
			if (data & 0x02)
				dstart = (dstart & 0xff00) | ((dstart + dyadv) & 0xff);
			else
				dstart += dyadv;
		}
	}

	return accesses;
}

void CWilliamsMachine::ExecuteBlit(u8 data)
{
	int sstart, dstart, w, h, accesses;

	/* compute the starting locations */
	sstart = (BlitterSrcHi << 8) + BlitterSrcLo;
	dstart = (BlitterDestHi << 8) + BlitterDestLo;

	/* compute the width and height */
	w = BlitterWidth ^ 4;
	h = BlitterHeight ^ 4;

	/* adjust the width and height */
	if (w == 0) w = 1;
	if (h == 0) h = 1;
	if (w == 255) w = 256;
	if (h == 255) h = 256;

	/* do the actual blit */
	accesses = blitter_core(sstart, dstart, w, h, data);
/*message("---------- Blit $%02X--------------PC: $%04X\n",data,CPU[0]->PC());
message("Source : %02X %02X\n",BlitterSrcHi,BlitterSrcLo);
message("Dest   : %02X %02X\n",BlitterDestHi,BlitterDestLo);
message("W H    : $%02X $%02X (%d,%d)\n",BlitterWidth,BlitterHeight,BlitterWidth^4,BlitterHeight^4);
message("Mask   : $%02X\n",BlitterMask);*/
}

CWilliamsMachine::CWilliamsMachine()
{
}

CWilliamsMachine::~CWilliamsMachine()
{
}

int CWilliamsMachine::Init()
{
CDefenderMachine::Init();
MainCPU->SetOp(ReadMainCPU);
MainCPU->SetRead(ReadMainCPU);
MainCPU->SetWrite(WriteMainCPU);
if(RomSet->GetFlags() & F_MUXINPUT)
	{
	PIA[0]->InputAFunc = PIA0_InputA_Mux;
	PIA[0]->OutputCB2Func = PIA0_OutputCB2_Mux;
	}
return(0);
}

void CWilliamsMachine::Reset()
{
CDefenderMachine::Reset();
BankPointer = VRAM;
BlitterMask = 0;
BlitterSrcHi = BlitterSrcLo = 0;
BlitterDestHi = BlitterDestLo = 0;
BlitterHeight = BlitterWidth = 0;
ActivePort = 0;
}

void CWilliamsMachine::Line(u32 mainc,u32 soundc,int irq)
{
CDefenderMachine::Line(mainc,soundc,irq);
}

void CWilliamsMachine::SaveState(CState *s)
{
CDefenderMachine::SaveState(s);
s->Write8(BlitterMask);
s->Write8(BlitterSrcHi);
s->Write8(BlitterSrcLo);
s->Write8(BlitterDestHi);
s->Write8(BlitterDestLo);
s->Write8(BlitterHeight);
s->Write8(BlitterWidth);
}

void CWilliamsMachine::LoadState(CState *s)
{
CDefenderMachine::LoadState(s);
BlitterMask = s->Read8();
BlitterSrcHi = s->Read8();
BlitterSrcLo = s->Read8();
BlitterDestHi = s->Read8();
BlitterDestLo = s->Read8();
BlitterHeight = s->Read8();
BlitterWidth = s->Read8();
BankPointer = VRAM;
if(BankRegister & 1)
	BankPointer = MainROM + 0x10000;
}

u8 CWilliamsMachine::ReadMainCPU(void *user,u32 addr)
{
CWilliamsMachine *M = (CWilliamsMachine*)user;

switch(addr & 0xF000)
	{
	case 0x0000:
	case 0x1000:
	case 0x2000:
	case 0x3000:
	case 0x4000:
	case 0x5000:
	case 0x6000:
	case 0x7000:
	case 0x8000:
		return(M->BankPointer[addr]);
	case 0x9000:
		if(addr >= 0x9800)
			return(M->MainRAM[addr - 0x9800]);
		return(M->BankPointer[addr]);
	case 0xA000:
	case 0xB000:
		return(M->MainRAM[addr - 0x9800]);
	case 0xC000:
		if(addr >= 0xCC00)						//nvram read
			return(M->NVRAM[addr & 0x3FF]);
		if((addr & 0xFF00) == 0xCB00)			//scanline pos read
			return((u8)M->Scanline);
		if((addr & 0xFFF4) == 0xC804)			//pia read
			{
			if(addr & 8)				//pia 1 read
				return(M->PIA[1]->Read(addr & 3));
			else							//pia 0 read
				return(M->PIA[0]->Read(addr & 3));
			}
		break;
	case 0xD000:
	case 0xE000:
	case 0xF000:
		return(M->MainROM[addr]);
	}
message("unhandled read (main cpu, addr $%04X)\n",addr);
return(0);
}

void CWilliamsMachine::WriteMainCPU(void *user,u32 addr,u8 data)
{
CWilliamsMachine *M = (CWilliamsMachine*)user;

switch(addr & 0xF000)
	{
	case 0x0000:
	case 0x1000:
	case 0x2000:
	case 0x3000:
	case 0x4000:
	case 0x5000:
	case 0x6000:
	case 0x7000:
	case 0x8000:
		M->VRAM[addr] = data;
		return;
	case 0x9000:
		if(addr < 0x9800)
			{
			M->VRAM[addr] = data;
			return;
			}
	case 0xA000:
	case 0xB000:
		M->MainRAM[addr - 0x9800] = data;
		return;
	case 0xC000:								//palette/nvram/io
		if(addr < 0xC010)						//palette write
			{
			M->PaletteRAM[addr & 0xF] = data;
			M->WritePalette(addr,data);
			}
		else if((addr & 0xFF00) == 0xC900)				//rom/vram switch
			{
			//message("bankswitch = $%02X\n",data);
			M->BankRegister = data;
			M->BankPointer = M->VRAM;
			if(data & 1)
				M->BankPointer = M->MainROM + 0x10000;
			}
		else if((addr & 0xFFF8) == 0xCA00)	//blitter write
			{
			switch(addr & 7)
				{
				case 0:	M->ExecuteBlit(data);			return;
				case 1:	M->BlitterMask = data;			return;
				case 2:	M->BlitterSrcHi = data;			return;
				case 3:	M->BlitterSrcLo = data;			return;
				case 4:	M->BlitterDestHi = data;		return;
				case 5:	M->BlitterDestLo = data;		return;
				case 6:	M->BlitterHeight = data;		return;
				case 7:	M->BlitterWidth = data;			return;
				}
			}
		else if(addr == 0xCBFF)				//watchdog
			{
			if(data == 0x39)					//check for specific value before allowing write
				M->Watchdog->Write(W_CPU1);
			else
				message("watchdog write bad byte = $%02X\n",data);
			}
		else if(addr >= 0xCC00)				//nvram write
			{
			if(M->RomSet->GetFlags() & F_NVRAM8BIT)
				M->NVRAM[addr & 0x3FF] = data;
			else
				M->NVRAM[addr & 0x3FF] = data | 0xF0;
			}
		else if((addr & 0xFFF4) == 0xC804)
			{
			if(addr & 8)						//pia 1 write
				M->PIA[1]->Write(addr & 3,data);
			else									//pia 0 write
				M->PIA[0]->Write(addr & 3,data);
			}
		else
			message("unhandled io write (main cpu, addr $%04X = $%02X)\n",addr,data);
		return;
	default:
		message("unhandled write (main cpu, addr $%04X = $%02X)\n",addr,data);
		return;
	}
}

u8 CWilliamsMachine::PIA0_InputA_Mux(void *user)
{
CWilliamsMachine *M = (CWilliamsMachine*)user;

if(M->ActivePort == 0)
	return(M->InputData[0]);
return(M->InputData[3]);
}

void CWilliamsMachine::PIA0_OutputCB2_Mux(void *user,u8 data)
{
CWilliamsMachine *M = (CWilliamsMachine*)user;

M->ActivePort = data;
}
